{include file="common/header"}
<style type="text/css">
.el-code-header{height: 35px;line-height: 35px;background: #565656;overflow: hidden;}
.el-code-header .item{float: left;padding: 0 16px;font-size: 14px;text-align: center;color: #fff;border-right: 1px solid #4c4c4c;cursor: pointer;}
.el-code-header .item:hover {background: #2f2f2f;}
.el-code-header .item i{margin-right: 6px;font-size: 18px}
.el-code-item{color: #fff;font-size: 14px;}
.el-code-item img{vertical-align: middle;width: 18px;height: 18px;margin-right: 8px;margin-bottom: 4px;}
.el-code-additem{height: 35px;line-height: 35px;font-size: 14px;cursor: pointer;}
.el-code-additem i{margin-right: 6px;font-size: 18px;}
.el-code-content{height: 100%;display: flex;}
.el-code-public{background: #222;padding: 5px;width: 270px;height: -moz-calc(100% - 45px);height: -webkit-calc(100% - 45px);height: calc(100% - 45px);overflow: auto;}
.el-code-public .el-tree{background: #222;}
.el-code-public .custom-tree-node{width: 100%}
.el-code-public .el-tree .el-code-item .el-input{width: 120px}
.el-code-public .el-tree .el-code-item .el-input input{height: 24px;background: #555;padding: 0 6px;border: 1px solid #409EFF;color: #fff}
.el-code-public .el-tree .el-code-item i{font-size: 18px;font-weight: 800;}
.el-code-public .el-tree .el-code-item .el-icon-check{color: #67C23A}
.el-code-public .el-tree .el-code-item .el-icon-close{color: #F56C6C}
.el-code-public .el-tree-node:focus>.el-tree-node__content{background-color: transparent;}
.el-code-public .el-tree-node__content{height: 27px;}
.el-code-public .el-tree-node__content:hover{background-color: #2f2f2f;}
.el-code-editor{height: 100%;width: -moz-calc(100% - 270px);width: -webkit-calc(100% - 270px);width: calc(100% - 270px);}
.el-code-editor .el-tabs__header{margin: 0 0 1px;}
.el-code-editor .el-tabs{background-color: #292929}
.el-code-editor .el-tabs .el-tabs__item{color: #fff}
.el-code-editor .el-tabs .el-tabs__item.is-active{color: #fff;background-color: #404040;border-bottom: 2px solid #20a53a}
.el-code-editor .el-tabs .el-tabs__item:hover{color: #fff;background-color: #404040;}
.el-code-editor .el-tabs--card>.el-tabs__header{border: 0}
.el-code-editor .el-tabs--card>.el-tabs__header .el-tabs__nav{border: 0}
.el-code-editor .el-tabs--card>.el-tabs__header .el-tabs__item{border-left:0;border-right: 1px solid #191919;}
.el-code-editor .el-tabs--card>.el-tabs__header i{position: relative;width: 18px!important;height: 18px!important;line-height: 18px!important;font-size: 18px!important;vertical-align: middle;top: -1px;right: -2px;margin-left: 5px;}
.el-code-editor .el-tabs--card>.el-tabs__header .not-saved .el-icon-close{color: rgb(204, 167, 0)}
.el-code-editor .el-tabs--card>.el-tabs__header .not-saved .el-icon-close:before{content: "\e7a1";}
.el-code-editor .el-tabs--card>.el-tabs__header .not-saved .el-icon-close:hover{color: #fff}
.el-code-editor .el-tabs--card>.el-tabs__header .not-saved .el-icon-close:hover:before{content:"\e6db"}
.el-code-editor .el-empty{height: 100%;background-color: #444}
#el-code{display: none}
#el-code-footer{display: none}
.el-code-editor-selected #el-code{display: block;width: 100%;height: -moz-calc(100% - 110px);height: -webkit-calc(100% - 110px);height: calc(100% - 110px);}
.el-code-editor-selected #el-code-footer{display: block;height: 35px;line-height: 35px;background-color: #444;color: #fff}
.el-code-editor-selected #el-code-footer .item{display: inline-block;padding: 0 16px;font-size: 14px;}
.el-code-editor-selected #el-code-footer .right{float: right;}
.el-code-editor-selected #el-code-footer .right .item{border-right: 1px solid #505050;}
.el-code-fast .item{line-height: 25px;color: #333;font-size: 0;margin-bottom: 5px;}
.el-code-fast .item span{border: 1px solid #ccc;height: 25px;padding: 0 10px;text-align: center;display: inline-block;border-radius: 2px;font-size: 12px;font-weight: 600;}
.el-code-fast .item b{padding: 0 5px;font-size: 15px;font-weight: 600;}
.el-code-fast .item i{padding-left: 25px;font-size: 13px;}
.el-code-right{position: fixed;width: 150px;padding: 5px 10px;border: 1px solid #ececec;background: #fff;border-radius: 2px;}
.el-code-right li{cursor: pointer;list-style-type: none;margin: 0;padding: 0;width: 150px;height: 30px;font-size: 14px;line-height: 30px;box-sizing: border-box;position: relative;color: #555;}
.el-code-right li i{vertical-align: middle;margin-right: 20px;font-size: 20px}
</style>
<div id="app" v-cloak>
    <div class="el-code-header">
        <el-popover
            placement="bottom"
            width="150"
            trigger="manual"
            v-model="addFileVisible">
            <div class="el-code-additem" @click="fileAdd('folder')"><i class="el-icon-folder-add"></i>新建文件夹</div>
            <div class="el-code-additem" @click="fileAdd('file')"><i class="el-icon-document"></i>新建文件</div>
            <div class="item" slot="reference" @click="addFileVisible = !addFileVisible"><i class="el-icon-plus"></i>新建</div>
        </el-popover>
        <div class="item" @click="fileUpdate()"><i class="el-icon-tickets"></i>保存</div>
        <div class="item" @click="fileAllUpdate()"><i class="el-icon-document-copy"></i>全部保存</div>
        <div class="item" @click="getData()"><i class="el-icon-refresh"></i>刷新</div>
        <div class="item" @click="search()"><i class="el-icon-search"></i>搜索</div>
        <div class="item" @click="replace()"><i class="el-icon-set-up"></i>替换</div>
        <el-popover
            placement="bottom"
            width="200"
            trigger="click">
            <el-input v-model="dumpRow" @input="dump()"></el-input>
            <div class="item" slot="reference"><i class="el-icon-bottom-left"></i>跳转行</div>
        </el-popover>
        <el-popover
            placement="bottom"
            width="200"
            trigger="click">
            <el-input-number v-model="fontSize" controls-position="right" :min="12" :max="40"></el-input-number>
            <div class="item" slot="reference"><i class="el-icon-reading"></i>字体</div>
        </el-popover>
        <el-popover
            placement="bottom"
            width="250"
            trigger="hover">
            <div class="el-code-fast">
                <div class="item"><span>Ctrl</span><b>+</b><span>S</span><i>保存文件</i></div>
                <div class="item"><span>Ctrl</span><b>+</b><span>C</span><i>复制内容</i></div>
                <div class="item"><span>Ctrl</span><b>+</b><span>X</span><i>剪切内容</i></div>
                <div class="item"><span>Ctrl</span><b>+</b><span>V</span><i>粘贴内容</i></div>
                <div class="item"><span>Ctrl</span><b>+</b><span>A</span><i>全选内容</i></div>
                <div class="item"><span>Ctrl</span><b>+</b><span>Z</span><i>撤销操作</i></div>
                <div class="item"><span>Ctrl</span><b>+</b><span>Y</span><i>反撤销操作</i></div>
                <div class="item"><span>Ctrl</span><b>+</b><span>H</span><i>搜索内容</i></div>
                <div class="item"><span>Esc </span><i>退出搜索、取消自动提示</i></div>
            </div>
            <div class="item" slot="reference"><i class="el-icon-question"></i>快捷键</div>
        </el-popover>
    </div>
    <div 
        class="el-code-content" 
        v-loading="loading"
        element-loading-text="加载中..."
        element-loading-spinner="el-icon-loading"
        element-loading-background="rgba(0, 0, 0, 0.8)">
        <div class="el-code-public">
            <el-tree
                :data="fileList"
                node-key="path"
                ref="tree"
                expand-on-click-node
                @node-click="readContent">
                <span class="custom-tree-node" slot-scope="{ node, data }" @contextmenu.prevent.stop="rightClick(node,data,$event)" @click="rightClickVisible = false" :ref="data.path">
                    <span class="el-code-item">
                        <img :src="cover(data.type)" />
                        <template v-if="! data.isNew && ! data.rename">{{ data.name }}</template>
                        <template v-if="data.isNew">
                            <el-popover
                                placement="top-start"
                                width="100"
                                trigger="manual"
                                v-model="data.errorName"
                                :content="data.repeatTips">
                                <el-input 
                                    slot="reference" 
                                    @keyup.enter.native="fileSave(node, data)" 
                                    v-model="data.name" 
                                    :ref="data.path + data.name">
                                </el-input>
                            </el-popover>
                            <i class="el-icon-close" @click.stop="fileRDelete(node, data)"></i>
                            <i class="el-icon-check" @click.stop="fileSave(node, data)"></i>
                        </template>
                        <template v-if="data.rename">
                            <el-popover
                                placement="top-start"
                                width="100"
                                trigger="manual"
                                v-model="data.errorName"
                                :content="data.repeatTips">
                                <el-input 
                                    slot="reference" 
                                    @keyup.enter.native="fileRename(node, data)" 
                                    v-model="data.newname" 
                                    :ref="data.path + data.name">
                                </el-input>
                            </el-popover>
                            <i class="el-icon-close" @click.stop="data.rename = ! data.rename "></i>
                            <i class="el-icon-check" @click.stop="fileRename(node, data)"></i>
                        </template>
                    </span>
                </span>
            </el-tree>
        </div>
        <div 
            class="el-code-editor" 
            :class="{'el-code-editor-selected': selected != ''}">
            <el-tabs 
                v-if="cacheList.length > 0" 
                v-model="cachePath" 
                type="card" 
                @tab-click="cacheClick">
                <el-tab-pane
                    class="not-saved"
                    v-for="(item, index) in cacheList"
                    :key="item.path"
                    :name="item.path">
                    <span slot="label" class="el-code-item" :class="{'not-saved': !item.isSave}">
                        <img :src="cover(item.type)" />
                        <span>{{ item.name }}</span>
                        <i v-if="item.saveLoading" class="el-icon-loading"></i>
                        <i v-else class="el-icon-close" @click.stop="cacheRemove(item.path)"></i>
                    </span>
                </el-tab-pane>
            </el-tabs>
            <div id="el-code" :style="{fontSize: fontSize + 'px'}"></div>
            <div id="el-code-footer">
                <span class="item">文件位置：{{selected.path}}</span>
                <div class="right">
                    <span class="item">行{{selected.row}} ， 列{{selected.column}}</span>
                    <span class="item">空格：4</span>
                    <span class="item">编码：UTF-8</span>
                    <span class="item">语言：{{selected.type}}</span>
                </div>
            </div>
            <el-empty description="未选择文件"></el-empty>
        </div>
    </div>
    <ul ref="coderight" class="el-code-right" style="left: 0;top: 0" :style="{zIndex: rightClickVisible ? 1024 : -1}">
        <template v-if="rightClickRow.type === 'folder'">
            <li @click="fileAdd('folder',rightClickRow)"><i class="el-icon-folder-add" style="color: #d6ab34"></i>新建文件夹</li>
            <li @click="fileAdd('file',rightClickRow)"><i class="el-icon-document-add" style="color: #cccccc"></i>新建文件</li>
        </template>
        <li @click="fileRenameTo()"><i class="el-icon-files" style="color: #409EFF"></i>重命名</li>
        <li @click="fileDelete()"><i class="el-icon-close" style="color: #F56C6C"></i>删除</li>
    </ul>
</div>
<script src="/plugins/code/static/js/ace.js" type="text/javascript" charset="utf-8"></script>
<script>
    new Vue({
        el: '#app',
        data() {
            return {
                url: "code/index/index",
                editUrl: "code/index/update",
                addUrl: "code/index/save",
                delUrl: "code/index/delete",
                renameUrl: "code/index/rename",
                contentUrl: "code/index/content",
                loading: false,
                dumpRow: 0,
                fontSize: 16,
                drawerHeight: window.innerHeight - 40 + 'px',
                coverArr: ['css','folder','folder-open','html','image','javascript','php','sql','mp4','zip','xml','txt'],
                fileList: [],
                cachePath: "",
                cacheList: [],
                editor: {},
                EditSession: require("ace/edit_session").EditSession,
                UndoManager: require("ace/undomanager").UndoManager,
                addFileVisible: false,
                rightClickVisible: false,
                rightClickRow: {},
                rightClickNode: {},
            }
        },
        computed: {
            /**
             * 当前行
             */
            row() {
                let index = common.arrayIndex(this.cacheList, this.cachePath, 'path');
                return index != -1 ? this.cacheList[index] : '';
            },
            /**
             * 选中
             */
            selected() {
                if (this.row != '') {
                    let cursor = this.editor.selection.getCursor();
                    let selected = this.row;
                    selected.row = cursor.row + 1;
                    selected.column = cursor.column;
                    return selected;
                } else {
                    return "";
                }
            },
        },
        mounted() {
            let self = this;
            self.editor = ace.edit("el-code");// 初始化
            self.editor.setTheme("ace/theme/monokai");// 设置主题
            self.editor.setShowPrintMargin(false); // 设置打印边距可见性
            // 内容变更
            self.editor.on('change', function (){
                self.row.isSave = false;
                self.row.c_content = self.editor.getValue();
            });
            self.editor.commands.addCommand({
                name: '保存内存',
                bindKey: {win: 'Ctrl-S',  mac: 'Command-S'},
                exec: function(editor) {
                    self.fileUpdate();
                }
            });
        },
        created() {
            this.getData();
        },
        methods: {
            /**
             * 获取侧边列表
             */
             getData() {
                let self = this;
                self.loading = true;
                request.post(self.url, {}, function(res) {
                    if (res.status === 'success') {
                        self.fileList = res.data;
                    } else {
                        self.$notify({showClose: true, message: res.message, type: 'error'});
                    }
                    self.loading = false;
                })
            },
            /**
             * 右击文件/文件夹
             */
            rightClick(node, data, event) {
                this.$refs.coderight.style.left = event.x + 'px';
                this.$refs.coderight.style.top  = event.y + 'px';
                this.rightClickVisible = true;
                this.rightClickRow = data;
                this.rightClickNode = node;
                document.addEventListener("click", this.listenEvent);
            },
            listenEvent() {
                this.rightClickVisible = false;
                document.removeEventListener("click", this.listenEvent);
            },
            /**
             * 读取文件内容
             */
            readContent(data) {
                let self = this;
                let notCode = ['image', 'zip', 'mp4'];
                if (data.type !== 'folder') {
                    if (notCode.indexOf(data.type) === -1) {
                        if (! data.isNew && !data.rename) {
                            let index = common.arrayIndex(self.cacheList, data.path, 'path');
                            if (index === -1) {
                                self.loading = true;
                                request.post(self.contentUrl, {filePath: data.path}, function(res) {
                                    if (res.status === 'success') {
                                        // 设置缓存
                                        let obj = JSON.parse(JSON.stringify(data));
                                        obj.content = new self.EditSession(res.data);
                                        // 撤销管理
                                        obj.content.setUndoManager(new self.UndoManager);
                                        // 追加缓存
                                        self.cacheList.push(obj);
                                        self.cachePath = data.path;
                                    } else {
                                        self.$notify({showClose: true, message: res.message, type: 'error'});
                                    }
                                    self.loading = false;
                                })
                            } else {
                                self.cachePath = data.path;
                            }
                        }
                    } else {
                        self.$notify({showClose: true, message: '该文件格式不支持在线编辑!', type: 'error'});
                    }
                }
            },
            /**
             * 点击缓存项
             */
            cacheClick(tab) {
                let index = common.arrayIndex(this.cacheList, tab['name'], 'path');
                this.readContent(this.cacheList[index]);
            },
            /**
             * 删除缓存项
             */
            cacheRemove(path) {
                let index = common.arrayIndex(this.cacheList, path, 'path');
                let newIndex = index - 1 < 0 ? 0 : index - 1;
                this.cacheList.splice(index, 1);
                if (typeof this.cacheList[newIndex] != 'undefined') {
                    if (path === this.cachePath) this.cachePath = this.cacheList[newIndex]['path'];
                }
            },
            /**
             * 文件保存
             */
            fileUpdate(row = '') {
                let self = this;
                let item = row === '' ? self.row : row;
                if (item != '') {
                    if (! item.saveLoading && ! item.isSave) {
                        item.saveLoading = true;
                        request.post(self.editUrl, {content: item.c_content, filePath: item.path}, function(res) {
                            if (res.status === 'success') {
                                item.isSave = true;
                            } else {
                                self.$notify({showClose: true, message: res.message, type: 'error'});
                            }
                            item.saveLoading = false;
                        })
                    }
                }
            },
            /**
             * 文件全部保存
             */
            fileAllUpdate() {
                let self = this;
                self.cacheList.forEach(function (item, index) {
                    self.fileUpdate(item);  
                })
            },
            /**
             * 文件伪删除(还未保存在数据库)
             */
            fileRDelete(node, data) {
                let parent = node.parent;
                let children = parent.data.children || parent.data;
                children.forEach(function (item, index) {
                    if (item.name === data.name && item.isNew) {
                        children.splice(index,1);
                    }
                });
            },
            /**
             * 文件删除
             */
            fileDelete() {
                let self = this;
                self.rightClickVisible = false;
                self.addFileVisible = false;
                self.$confirm('确定删除' + self.rightClickRow.path + '吗？', '', {
                    confirmButtonText: '确定',
                    cancelButtonText: '取消',
                    type: 'warning'
                }).then(() => {
                    request.post(self.delUrl, {filePath: self.rightClickRow.path, fileType: self.rightClickRow.type}, function(res){
                        if (res.status === 'success') {
                            let parent = self.rightClickNode.parent;
                            let children = parent.data.children || parent.data;
                            let index = -1;
                            children.forEach(function (item, index) {
                                if (item.path === self.rightClickRow.path) {
                                    children.splice(index,1);
                                }
                            });
                        } else {
                            self.$notify({ showClose: true, message: res.message, type: res.status});
                        }
                    });
                }).catch(() => {});
            },
            /**
             * 新增文件或文件夹
             */
            fileAdd(type, row = '') {
                let self = this;
                let item = {
                    name: '未命名', 
                    path: '', 
                    type: '', 
                    content: '', 
                    isSave: true, 
                    saveLoading: false,
                    isNew: true,
                };
                item.path = row === '' ? '' : row.path + '/';
                item.type = type === 'folder' ? 'folder' : '';
                if (row != '') {
                    row.children.forEach(function (item, index) {
                        if (item.isNew) {
                            row.children.splice(index, 1);
                        }
                    })
                    row.children.unshift(item);
                    self.$refs.tree.store.nodesMap[row.path].expanded = true;
                } else {
                    self.fileList.forEach(function (item, index) {
                        if (item.isNew) {
                            self.fileList.splice(index, 1);
                        }
                    })
                    self.fileList.unshift(item);
                }
                self.rightClickVisible = false;
                self.addFileVisible = false;
                self.$nextTick(function () {
                    // 选中
                    let el = self.$refs[item.path + item.name];
                    el.focus();
                    el.select();
                })
            },
            /**
             * 文件新增
             */
            fileSave(node, data) {
                let self = this;
                self.fileNameVerify(node, data);
                if (! data.errorName) {
                    self.loading = true;
                    request.post(self.addUrl, {filePath: data.path, fileName: data.name, fileType: data.type}, function(res) {
                        if (res.status === 'success') {
                            data.isNew = false;
                            data.path = res.data.path;
                            data.type = res.data.type;
                            self.getData();
                            self.readContent(data);
                        } else {
                            self.$notify({showClose: true, message: res.message, type: 'error'});
                        }
                        self.loading = false;
                    })
                }
            },
            /**
             * 文件重命名
             */
            fileRenameTo() {
                this.rightClickRow.rename = true;
                this.rightClickVisible = false;
                this.addFileVisible = false;
                this.$nextTick(function () {
                    // 选中
                    let el = this.$refs[this.rightClickRow.path + this.rightClickRow.name];
                    el.focus();
                    el.select();
                })
            },
            fileRename(node, data) {
                let self = this;
                self.fileNameVerify(node, data, 'newname');
                if (! data.errorName) {
                    request.post(self.renameUrl, {newname: data.newname, oldname: data.name, filePath: data.path}, function(res) {
                        if (res.status === 'success') {
                            if (self.cachePath == data.path) {
                                self.row.name = res.data.newname;
                                self.row.path = res.data.path;
                                self.cachePath = res.data.path;
                            }
                            data.name = res.data.newname;
                            data.path = res.data.path;
                        } else {
                            self.$notify({showClose: true, message: res.message, type: 'error'});
                        }
                        data.rename  = false;
                    })
                }
            },
            /**
             * 验证文件夹/文件名称
             */
            fileNameVerify(node, data, name = 'name') {
                let parent = node.parent;
                let children = parent.data.children || parent.data;
                let number = 0;
                console.log(data);
                let fileInfo = data[name].split('.');
                children.forEach(function (item, index) {
                    if (item[name] === data[name]) {
                        number++;
                    }
                });
                if (number > 1) {
                    data.errorName = true;
                    data.repeatTips = data.type === 'folder' ? '文件夹名称不能重复' : '文件名称不能重复';
                    return false;
                }
                if (fileInfo.length < 2 && data.type !== 'folder') {
                    data.errorName = true;
                    data.repeatTips = '请填写文件的后缀名';
                    return false;
                }
                data.errorName = false;
                data.repeatTips = '';
            },
            /**
             * 文件封面图
             */
            cover(type) {
                console.log(type)
                let cover = this.coverArr.indexOf(type) == -1 ? 'txt' : type;
                return "/plugins/code/static/img/" + cover + '.png';
            },
            /**
             * 搜索
             */
            search() {
                this.editor.execCommand('find')
            },
            /**
             * 替换
             */
            replace() {
                this.editor.execCommand('replace');
            },
            /**
             * 跳转行
             */
            dump() {
                this.editor.gotoLine(this.dumpRow);
            }
        },
        watch: {
            cachePath(path) {
                // 读取代码内容
                this.editor.setSession(this.row.content);
                // 设置代码语言
                let codeArr = ['css','html','javascript','json','php'];
                if (codeArr.indexOf(this.row.type) !== -1) {
                    this.editor.session.setMode("ace/mode/" + this.row.type);
                    this.editor.session.setUseWorker(false);
                }
            },
        }
    });
</script>
{include file="common/footer"}